package cc.idoone.spring_boot_hello.h2.jpa;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * 类似与dao层接口的操作数据库的接口，这个接口要继承 PagingAndSortingRepository 才能实现对数据库的 crud 操作
 * <h3>JPA</h3>
 * <p>
 * 我们有两种方法可以指定查询语句： <br/>
 * （1）根据JPA所提供的命名规范，来命名自己的方法，那么Spring Data JPA就会根据它的解析规范来进行处理。 <br/>
 * （2）在声明的方法上面使用@Query 注解，并自己提供一个查询语句，那么Spring Data JPA在处理的时候就会按照我们提供的语句来进行处理。<br/>
 * </p>
 * <h3>Spring Data JPA的方法名解析规范 </h3>
 * <p>
 * And — 等价于 SQL 中的 and 关键字，比如 findByUsernameAndPassword(String user, Striang pwd)； <br/>
 * Or — 等价于 SQL 中的 or 关键字，比如 findByUsernameOrAddress(String user, String addr)； <br/>
 * Between — 等价于 SQL 中的 between 关键字，比如 findBySalaryBetween(int max, int min)； <br/>
 * LessThan — 等价于 SQL 中的 “<”，比如 findBySalaryLessThan(int max)； <br/>
 * GreaterThan — 等价于 SQL 中的”>”，比如 findBySalaryGreaterThan(int min)； <br/>
 * IsNull — 等价于 SQL 中的 “is null”，比如 findByUsernameIsNull()； <br/>
 * IsNotNull — 等价于 SQL 中的 “is not null”，比如 findByUsernameIsNotNull()； <br/>
 * NotNull — 与 IsNotNull 等价； <br/>
 * Like — 等价于 SQL 中的 “like”，比如 findByUsernameLike(String user)； <br/>
 * NotLike — 等价于 SQL 中的 “not like”，比如 findByUsernameNotLike(String user)； <br/>
 * OrderBy — 等价于 SQL 中的 “order by”，比如 findByUsernameOrderBySalaryAsc(String user)； <br/>
 * Not — 等价于 SQL 中的 “！ =”，比如 findByUsernameNot(String user)； <br/>
 * In — 等价于 SQL 中的 “in”，比如 findByUsernameIn(Collection userList) ，方法的参数可以是 Collection 类型，也可以是数组或者不定长参数； <br/>
 * NotIn — 等价于 SQL 中的 “not in”，比如 findByUsernameNotIn(Collection userList) ，方法的参数可以是 Collection 类型，也可以是数组或者不定长参数；<br/>
 * </p>
 *
 * @auther hemj
 * @since 2017/9/23
 */
@Repository
@Transactional(transactionManager = "h2JpaManager", propagation = Propagation.SUPPORTS)
public interface JpaStaffDao extends PagingAndSortingRepository<StaffBo, String> {

	/**
	 * 需要覆盖父接口的方法声明 @Transactional(transactionManager = "h2JpaManager") 才能生效
	 */
	StaffBo save(StaffBo staff);

	/**
	 * 父接口的方法默认使用: transactionManager = "transactionManager"
	 */
	Iterable<StaffBo> findAll();

	/**
	 * 根据 getBy 后面的属性名查询
	 * <p>
	 * find、findBy、read、readBy、get、getBy 查看 JPA 命名规范
	 * </p>
	 * <p>
	 * findByUserName findBy[FieldName] 属性名称需要与实体类一致，否则会抛出异常 No property name found for type StaffBo
	 * </p>
	 */
	StaffBo findByUserName(String userName);

	/**
	 * Enabling ignoring case for an individual property
	 */
	List<StaffBo> findByUserNameIgnoreCase(String name);

	/**
	 * Enabling ignoring case for all suitable properties
	 */
	List<StaffBo> findByUserIdAndUserNameAllIgnoreCase(String id, String name);

	/**
	 * 根据query语句查询
	 * <p>
	 * <p>注意: hibernate.hql 是对类进行查询</p>
	 */
	@Query(value = "select new StaffBo(userId, userName, age) from StaffBo c where c.age > :age")
	List<StaffBo> getByMinAge(@Param("age") int minAge);

	/**
	 * 以 ?1 来标记需要输入参数的地方，然后在方法中给出参数
	 */
	@Query("select new StaffBo(userId, userName, age) from StaffBo c where c.userId = ?1")
	StaffBo findByUserId(String id);

	/**
	 * 以 ?1 来标记需要输入参数的地方，然后在方法中给出参数
	 */
	@Query("select new StaffBo(userId, userName, age) from StaffBo a where a.age > ?1")
	Page<StaffBo> findByAgeGreaterThan(Integer age, Pageable pageable);

	/**
	 * 使用 @Modifying 将查询标识为修改
	 */
	@Modifying
	@Query("update StaffBo a set a.age = ?1 where a.age < ?2")
	int increaseAge(int after, int before);
}
